<template>
  <div id="app-root">
    <div class="game-top">
      <div class="view-center">
        <div class="top-nav">
          <div>当前积分</div>
          <div class="number">{{ score }}</div>
        </div>
        <div class="top-nav">
          <div>最高积分</div>
          <div class="number">{{ maxScore }}</div>
        </div>
      </div>
    </div>
    <div
      class="game-main"
      @mousedown="handleTouchstart"
      @mouseup="handleTouchend"
    >
      <div class="main-bor clearfix">
        <div class="item bgItem" v-for="(item, index) in nums" :key="index">
          <!-- 显示层 -->
          <div :class="['item-li', 'view-center', `c${item}`]">
            {{ item }}
          </div>
          <!-- 移动层 -->
          <div
            :class="[
              'move-li',
              'item-li',
              'view-center',
              `c${copyNums[index]}`,
            ]"
          >
            {{ copyNums[index] }}
          </div>
        </div>
      </div>
    </div>
    <el-button v-if="!isOnGame" @click="startGame">开始</el-button>
    <el-button v-if="isOnGame" @click="handleRestGame">重置</el-button>

    <el-dialog v-model="dialogVisible" title="提示" width="30%">
      <span>{{ dialogMessage }}</span>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="dialogVisible = false">取 消</el-button>
          <el-button type="primary" @click="dialogConfirm">确 定</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, nextTick, onMounted } from "vue";
import { ElButton, ElDialog } from "element-plus";

// 合并动画
const animationData = ref([]);
// 移动动画
const animationDataNum = ref([]);
const animationRef = ref();

const numPox = ref([]);
const start = ref({
  x: 0,
  y: 0,
}); // 记录移动端触摸起始点
const nums = ref([]); // 记录16个框格的属性
const copyNums = ref([]); // 移动格子
// 是否移动中
const isMove = ref(false);
const score = ref(0); // 当前积分
const maxScore = ref(0); // 最高积分
// 是否游戏中
const isOnGame = ref(false);

const dialogVisible = ref(false);
const dialogMessage = ref("");
const dialogCallback = ref(null);

onMounted(() => {
  document.addEventListener("keyup", keyDown);
  nums.value = Array(16).fill("");
  init();
  console.log("打印nums.value===", nums.value);
  // const info = document.getElementsByClassName("bgItem");

  setTimeout(() => {
    let info = document.querySelectorAll(".bgItem");
    info.forEach((el, index) => {
      numPox.value[index] = el.getBoundingClientRect();
    });
  }, 100);
  // nextTick(() => {
  //   nums.value = Array(16).fill('');
  //   init();
  //   console.log('打印nums.vlaue===',nums.vlaue)
  //   let info = document.querySelectorAll(".bgItem");
  //   console.log('打印info===',info)
  //   info.forEach((el, index) => {
  //     numPox.value[index] = el.getBoundingClientRect();
  //   });
  // });
});

// 初始化游戏数据
const init = () => {
  maxScore.value = localStorage.getItem("maxScore") || 0;
  startGame();
};

// 获取对应的位置
const getIndexPos = (index) => {
  return numPox.value[index];
};

// 重置游戏
const handleRestGame = () => {
  showDialog("确定重置", "确定重置当前游戏！", () => {
    startGame();
  });
};

// 领取奖品
const handleReward = (item) => {
  showDialog("恭喜", `恭喜你获得了${item.name}`, () => {
    item.is_receive = true;
  });
};

// 获取空白数组
const blankIndex = () => {
  let arr = [];
  nums.value.forEach((i, j) => {
    if (i === "") arr.push(j);
  });
  return arr;
};

/* 在一个随机的空白位添加数字 */
const randomAdd = (count = 1) => {
  let arr = shuffle(blankIndex());
  let random = [2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 8];
  while (count--) {
    let arrIndex = arr.pop();
    // 延时100毫秒添加
    setTimeout((_) => {
      nums.value.splice(arrIndex, 1, random[Math.floor(Math.random() * 10)]);
      // 计算分数
      getScore();
    }, 100);
  }
};

/* 打乱数组 */
const shuffle = (arr) => {
  let l = arr.length,
    j;
  while (l--) {
    j = parseInt(Math.random() * l);
    [arr[l], arr[j]] = [arr[j], arr[l]];
  }
  return arr;
};

// 重置游戏
const reset = () => {
  score.value = 0;
  nums.value = Array(16).fill("");
  copyNums.value = [...nums.value];
};

// 开始触摸
const handleTouchstart = (e) => {
  start.value.x = e.clientX;
  start.value.y = e.clientY;
};

// 触摸结束
const handleTouchend = (e) => {
  let curPoint = e;
  let x = curPoint.clientX - start.value.x,
    y = curPoint.clientY - start.value.y,
    xx = Math.abs(x),
    yy = Math.abs(y),
    i = 0;
  // 移动范围太小 不处理
  // 0左  2右  3下 1 上
  if (xx < 50 && yy < 50) return;
  if (xx >= yy) {
    // 横向滑动
    i = x < 0 ? 0 : 2;
  } else {
    // 纵向滑动
    i = y < 0 ? 3 : 1;
  }
  handle(i);
};

const keyDown = (e) => {
  console.log("打印e===", e);
  // 左上右下 分别转置0 3 2 1 次
  const map = { 37: 0, 38: 3, 39: 2, 40: 1 };
  if (!(e.keyCode in map)) return;
  handle(map[e.keyCode]);
};
// const handle = (i) => {
//   move(i);
//   save();
//   isPass(); // 判断是否过关
// };

// 触摸完成移动方块
const handle = (i) => {
  if (!isMove.value) {
    copyNums.value = [...nums.value];
    move(i);
  }
};

/* 把数组矩阵，旋转n次，旋转后统一按照左移处理 */
const transposeMatrix = (arr, n) => {
  n = n % 4;
  if (n === 0) return arr;
  let l = arr.length,
    d = Math.sqrt(l),
    tmp = [];
  for (let i = 0; i < d; i += 1)
    for (let j = 0; j < d; j += 1) tmp[d - i - 1 + j * d] = arr[i * d + j];
  if (n > 1) tmp = transposeMatrix(tmp, n - 1);
  return tmp;
};

/* 移动方块 i:旋转次数 */
const move = (i) => {
  isMove.value = true;
  let indexs = transposeMatrix(Object.keys(String(Array(17))).map(Number), i); // 记录旋转前的位置索引
  let tmp = transposeMatrix([...nums.value], i); // 矩阵旋转
  let hasMove = false; // 判断是否有移动
  let hasCombin = {}; // 记录合并索引
  tmp.forEach((item, index) => {
    let newIndex = 0, // 方块挪动后的索引 （转换后的索引）
      oldIndex = indexs[index], // 换算到转换前的索引
      thisMoved = false, // 此方块有数字，且被移动了 标记  需要应用动画
      combinNum = 0; // 方块若有合并，记录合并后的数字
    while (index % 4 && item !== "") {
      if (tmp[index - 1] === "") {
        // 当前位置的前一位置为空,交换俩位置
        tmp[index - 1] = item;
        tmp[index] = "";
        hasMove = true;
        thisMoved = true;
        newIndex = index - 1;
      } else if (
        tmp[index - 1] === item &&
        !hasCombin[index] &&
        !hasCombin[index - 1] &&
        item < 2048
      ) {
        // 当前位置与前一位置数字相同，合并到前一位置，然后清空当前位置
        item *= 2;
        tmp[index - 1] = item;
        tmp[index] = "";
        hasMove = true;
        thisMoved = true;
        combinNum = item;
        hasCombin[index - 1] = true; // 记录合并位置
        newIndex = index - 1;
      } else {
        break;
      }
      index--;
    }
    thisMoved && moveNode(oldIndex, indexs[newIndex], combinNum, i);
  });
  // 移动动画完成后还原
  setTimeout((_) => {
    nums.value = [...transposeMatrix(tmp, 4 - i)]; // 转置回去，把数据还给nums
    if (hasMove) {
      randomAdd();
    } else {
      gameOver();
    }
    isMove.value = false;
  }, 200);
};

// 索引index的元素移动到nextIndex
const moveNode = (index, nextIndex, combinNum, i) => {
  console.log("打印index===", index);
  let nowPos = getIndexPos(index);
  let nextPos = getIndexPos(nextIndex);
  console.log("打印nextPos===", nextPos);
  const moveElement = document.querySelector(
    `.bgItem:nth-child(${index + 1}) .item-li`
  );
  if (moveElement) {
    moveElement.style.zIndex = 1000;
    console.log("打印===", moveElement, index, i, nextPos, nowPos);
    moveElement.style.transition = "transform 0.2s ease";
    if (i === 0 || i === 2) {
      console.log("打印左右移动距离===", nextPos.left - nowPos.left);
      moveElement.style.transform = `translateX(${
        nextPos.left - nowPos.left
      }px)`;
    } else {
      console.log("打印上下移动距离===", nextPos.top - nowPos.top);
      moveElement.style.transform = `translateY(${nextPos.top - nowPos.top}px)`;
    }
    setTimeout(() => {
      // moveElement.style.zIndex = 0;
      moveElement.style.transition = "";
      moveElement.style.transform = "";
    }, 200);
  }
};

// 开始游戏
const startGame = () => {
  // 重置分数-数据
  reset();
  // 添加方块
  randomAdd(2);
  // 游戏标识
  isOnGame.value = true;
};

// 判断游戏结束
const gameOver = () => {
  let isOver = true;
  for (let index = 0; index < nums.value.length; index++) {
    let item = nums.value[index] || 0;
    if (item == 0) {
      isOver = false;
      break;
    }
    if (item >= 2048) {
      continue;
    }
    let mod = index % 4;
    if (mod == 0) {
      if (
        nums.value[index - 4] == item ||
        nums.value[index + 4] == item ||
        nums.value[index + 1] == item
      ) {
        isOver = false;
        break;
      }
    } else if (mod == 3) {
      if (
        nums.value[index - 4] == item ||
        nums.value[index + 4] == item ||
        nums.value[index - 1] == item
      ) {
        isOver = false;
        break;
      }
    } else {
      if (
        nums.value[index - 4] == item ||
        nums.value[index + 4] == item ||
        nums.value[index - 1] == item ||
        nums.value[index + 1] == item
      ) {
        isOver = false;
        break;
      }
    }
  }
  if (isOver) {
    // 游戏标识
    isOnGame.value = false;
    showDialog("游戏结束", "你已经无路可走了！", () => {
      reset();
    });
  }
};

const getScore = () => {
  let cScore = 0;
  nums.value.forEach((item) => {
    if (item !== "") {
      cScore = cScore + Number(item);
    }
  });
  score.value = cScore;
  if (cScore > maxScore.value) {
    maxScore.value = cScore;
    localStorage.setItem("maxScore", cScore);
  }
};

const showDialog = (title, message, callback) => {
  dialogVisible.value = true;
  dialogMessage.value = message;
  dialogCallback.value = callback;
};

const dialogConfirm = () => {
  dialogVisible.value = false;
  if (dialogCallback.value) {
    dialogCallback.value();
  }
};
</script>

<style lang="scss" scoped>
#app-root {
  width: 100%;
  position: relative;
  display: flex;
  flex-direction: column;
  // justify-content: center;
  align-items: center;
  font-family: "Inknut Antiqua", "Clear Sans", "Helvetica Neue", Arial,
    sans-serif;
}

.game-top {
  width: 25%;
  margin: 20px 48px 48px 48px;
  background: #ffe4b3;
  border-radius: 20px;
  padding: 10px;

  .top-nav {
    padding-top: 10px;
    width: 178px;
    height: 90px;
    background: #f96e51;
    border: 4px solid rgba(177, 118, 78, 1);
    border-radius: 14px;
    text-align: center;
    font-size: 24px;
    color: rgba(255, 255, 255, 1);
    margin: 0 10px;

    .number {
      padding-top: 4px;
      color: #ffe4b3;
      font-size: 28px;
      font-weight: 600;
    }
  }
}

.game-n {
  margin-top: 20px;
  color: #f96e51;

  .view-flex__item:last-child {
    text-align: right;
  }
}

.game-slider {
  border: 4px solid #7c4d28;
  background: #5b3610;
  height: 28px;
  margin-top: 10px;
  border-radius: 20px;
  position: relative;

  .slider-w {
    background: #ffca00;
    height: 20px;
    border-radius: 20px;
    max-width: 100% !important;
  }

  .slider-p {
    position: absolute;
    top: -5px;
    left: 0;
    width: 100%;

    .view-flex__item {
      text-align: right;
      position: relative;

      .p-bx {
        width: 76px;
        text-align: center;
        position: absolute;
        top: 50px;
        right: -24px;

        img {
          width: 76px;
        }
      }
    }
  }
}

.game-main {
  width: 25%;
  margin: 0 48px;
  border-radius: 20px;
  overflow: hidden;

  .main-bor::after {
    content: "";
    display: table;
    clear: both;
  }

  .main-bor {
    border: 10px solid #f8f3e8;
    background: rgba(177, 118, 78, 0.3);
    border-radius: 20px;
    padding: 5px;

    .item {
      float: left;
      width: calc(25% - 10px);
      margin: 5px;
      padding-bottom: calc(25% - 10px);
      background: #f8f3e8;
      position: relative;
      .move-li {
        opacity: 0;
      }
      .item-li {
        // transition: transform 0.5s linear;
        position: absolute;
        width: 100%;
        height: 100%;
        font-size: 30px;
        font-weight: bold;
        color: #ffffff;

        &.c2 {
          background: #ffe4b3;
          color: #986d0f;
        }

        &.c4 {
          background: #f9ce7f;
          color: #986d0f;
        }

        &.c8 {
          background: #fcbd3b;
          color: #986d0f;
        }

        &.c16 {
          background: #f98e78;
        }

        &.c32 {
          background: #f96e51;
        }

        &.c64 {
          background: #f599b2;
        }

        &.c128 {
          background: #ec6187;
        }

        &.c256 {
          background: #1b67b3;
        }

        &.c512 {
          background: #3884cf;
        }

        &.c1024 {
          background: #9c79e0;
          font-size: 60px;
        }

        &.c2048 {
          background: #6d41c1;
          font-size: 60px;
        }
      }
    }
  }
}

::v-deep .rule-w {
  .u-popup__content {
    width: 80%;
    background: none;
  }

  .u-icon__icon {
    color: #fff !important;
  }

  .rule-main {
    background: #bc7638;
    padding: 20px;
    border-radius: 10px;
    max-height: 50vh;
    overflow-y: auto;
    font-size: 24px;

    .title {
      padding-bottom: 10px;
    }

    .text {
      padding-bottom: 10px;
      line-height: 44px;
    }
  }
}

.rule-open {
  padding: 10px;
  text-align: center;
}

.rule-tip {
  width: 200px;

  img {
    vertical-align: middle;
    margin: -4px 8px 0 0;
  }
}

#app-root button {
  width: 100px;
  height: 50px;
  // line-height: 50px;
  font-size: 20px;
  display: block;
  cursor: pointer;
  margin: 20px auto;
  background-color: #2daebf;
  border-color: #238896;
  color: white;
  box-shadow: 0 -2px 0 rgba(0, 0, 0, 0.2) inset;
  border-radius: 4px;
  transition: background-color 300ms ease-out;
  animation: bluePulse 2s 4s infinite;
}

.uni-stat-tooltip {
  max-width: 400px;
  max-height: 400px;
  overflow: scroll;
  word-break: break-word;
}

.view-center {
  display: flex;
  align-items: center;
  justify-content: center;
}

.view-flex {
  display: -webkit-box;
  display: -webkit-flex;
  display: flex;
}

.view-flex__item {
  -webkit-box-flex: 1;
  -webkit-flex: 1;
  flex: 1;
}

@keyframes zy {
  10% {
    transform: rotate(15deg);
  }

  20% {
    transform: rotate(-10deg);
  }

  30% {
    transform: rotate(5deg);
  }

  40% {
    transform: rotate(-5deg);
  }

  50%,
  100% {
    transform: rotate(0deg);
  }
}

.treasureBox {
  animation: zy 1.5s 0.15s linear infinite;
}
</style>
